<?php
###############   COPYLEFT GPLv3 LICENSE   ###############
##
## JFX Version 0.2.9
## Website Management Software
## www.jfxcms.com
##
## Copyright 2009 GPLv3 - http://www.opensource.org/licenses/gpl-3.0.html
##
## Anthony Gallon
## oi_antz@hotmail.com
##
## Permission is hereby granted to any person having a copy of this software
## to freely use and modify as required so long as the copyright notices
## and branding remain intact.
##
## Full license details available at http://www.jfxcms.com/license
##
###############   COPYLEFT GPLv3 LICENSE   ###############



/**
 *  Categories module
 *  Provides static methods for managing categories
 *  Requires front-end implementation
 */
class JFX_Categories
{
    protected $maxDepth = 4;
    protected $tablename = 'categories';

    public function __construct($tablename='categories', $DB)
   { // BEGIN function __contstruct
       $this->db = $DB;
   	$this->tablename = $tablename;
   	$this->maxDepth = 4;
   } // END function __contstruct

   public function isValidCategory($cat){
       $DB = $this->db;
       $cat = (int) $cat;
       if($DB->countRows($this->tablename, "id = '{$cat}' AND disabled = 0")==0) return false;
       else return true;
   }

   public function hasSubCats($cat){
       $subcats = $this->getSubCatsAsArray($cat, false);
       if(count($subcats)>0) return true;
       else return false;
   }

    /**
     *  Returns an array of rows from the categories table, where the parent id == $parentId
     */
    public function getSubCatsAsArray($parentId = 0, $recursive = false, $displayHidden=false)
    {
        $parentId = (int) $parentId;
        $DB = $this->db;
        $select = $DB->select();
        $select->from($this->tablename, '*');
        $select->order('sort ASC');
        $select->where('parent_id', $parentId);
        if($displayHidden !== true) $select->where('disabled', '0');
        $res = $DB->fetchAll($select);
        if(!is_array($res)) $res = array();
        if($recursive){
            foreach($res as $k=>$cat){
                $res[$k]['subcats'] = $this->getSubCatsAsArray($cat['id'], $recursive, $displayHidden);
            };
        };
        return $res;
    }

    /**
     *  Each time a cat is viewed, it is incremented to keep a statistic of popular cats
     */
    public function incrementCatViewedCounter($catId)
    {
        $catId = (int) $catId;
        if($catId == 0)return;
        $DB = $this->db;
        $select = $DB->select()
          ->from($this->tablename, 'num_views, last_day, num_days')
          ->where('id', $catId);
        $res = $DB->fetchRow($select);

        if($res['numDays']==0)$res['numDays']=1;
        $popularity = (($res['numViews']+1)/$res['numDays']);
        if($res['lastDay']+86400 <= time()){
            // count another day
            $bind = array('last_day'=>time(),
                          'num_days'=>$res['numDays']+1,
                          'num_views'=>$res['numViews']+1,
                          'popularity'=>$popularity);
        }else{
            $bind = array('num_views'=>$res['numViews']+1,
                          'popularity'=>$popularity);
        }
        $DB->update($this->tablename, $bind, 'id = '.intval($catId));
    }


    /**
     *  Orders the categories by the most popular, and limits them by the number that you provide
     */
    public function getMostPopularCategories($limit)
    {
        $DB = $this->db;
        $select = $DB->select()
          ->from($this->tablename, '*')
          ->where('depth', 1)
          ->order('popularity DESC')
          ->limit($limit);
        return $DB->fetchAll($select);
    }


    /**
     *  Returns the value of the title for the given $catId
     */
    public function getTitle($catId, $lang='en')
    {
        $DB = $this->db;
        $lang = preg_replace('/[^a-zA-Z0-9]/', '', $lang);
        $catId = (int) $catId;
        return $DB->oneValue($this->tablename, 'title_'.$lang, "id = '{$catId}'");
    }

    /**
     *  returns the row of data for the corresponding catId
     */
    public function getDetails($catId)
    {
        $DB = $this->db;
        $catId = (int) $catId;
        return $DB->fetchRow("SELECT * FROM $this->tablename WHERE id = '{$catId}'");
    }


    /**
     *  Returns the id of the category's parent
     */
    public function getParentId($catId)
    {
        $catId = (int) $catId;
        $DB = $this->db;
        return $DB->oneValue($this->tablename, 'parent_id', "id = '{$catId}'");
    }

    /**
     *  Returns the depth of the given category
     */
    public function getCategoryDepth($catId)
    {
        $catId = (int) $catId;
        $DB = $this->db;
        if($catId == 0)return 0;
        return $DB->oneValue($this->tablename, 'depth', "id = '{$catId}'");
    }

    /**
     *  Adds a category to the system
     */
    public function add($parentId, $title)
    {
        $parentId = (int) $parentId;
        if(!is_array($title)){
            $title = array('en'=>$title);
        };
        if(!isset($title['fr']))$title['fr'] = $title['en'];
        foreach($title as $k=>$v){
            $title[$k] = preg_replace('/"\'/', '', $v);
        };
        $DB = $this->db;
        $parentDepth = $this->getCategoryDepth($parentId);
        if($parentDepth >= 4){
            // max depth exceeded
            return false;
        };
        // find the sort value
        $subcats = $this->getSubcatsAsArray($parentId);
        $subcat = $subcats[count($subcats)-1];
        $newSort = $subcat['sort']+1;
        $bind = array('parent_id'=>$parentId,
                      'title_en'=>$title['en'],
                      'title_fr'=>$title['fr'],
                      'depth'=>($parentDepth+1),
                      'sort'=>$newSort
                      );
        $id = $DB->insert($this->tablename, $bind);
        $select = $DB->select()->from($this->tablename, 'COUNT(*)')->where('parent_id', $parentId);
        $numSubCats = $DB->fetchRow($select);
        $numSubCats = $numSubCats['COUNT(*)'];
        $DB->update($this->tablename, array('num_sub_cats'=>$numSubCats), 'id = '.$parentId);

        return $id;
    }



    /**
     *  Remove a category from the system, put its sub cats under a new parent
     *  if $newParentId == NULL, it will put the subcats under the natural parent
     */
    public function delete($catId, $newParentId=NULL)
    {
        $DB = $this->db;
        $catId = (int) $catId;
        $newParentId = (int) $newParentId;
        if($newParentId==NULL)$newParentId = $this->getParentId($catId);
        // move the items under the new category
        $depth = (int) $this->getCategoryDepth($catId);
        $catTree = $this->getAllParentCatIdsAsArray($newParentId);
        //Antz_Listings_Manage::moveCats($catId, $newParentId, $catTree, $depth);
        $parentId = (int) $this->getParentId($catId);
        $cats = $this->getSubCatsAsArray($catId);
//echo 'deleting '.$catId.', new parent: '.$parentId;
        foreach($cats as $cat){
            if(!$this->move($cat['id'], $newParentId)) return false;
        };
        $DB->delete($this->tablename, 'id = '.$catId);
        $select = $DB->select()->from($this->tablename, 'COUNT(*)')->where('parent_id', $parentId);
        $numSubCats = $DB->fetchRow($select);
        $numSubCats = $numSubCats['COUNT(*)'];
        $DB->update($this->tablename, array('num_sub_cats'=>$numSubCats), 'id = '.$parentId);
        return true;
    }

    /**
     *  Rename the category title
     */
    public function rename($id, $title, $lang='en')
    {
        $DB = $this->db;
        $id = (int) $id;
        $title = preg_replace('/"\'/', '', $title);
        $lang = preg_replace('/[^a-zA-Z0-9]/', '', $lang);
        $DB->update($this->tablename, array('title_'.$lang => $title), 'id = '.$id);
        return true;
    }

    /**
     *  Update the order of appearance of categories
     */
    public function reposition($id, $newPos)
    {
        $DB = $this->db;
        $id = (int) $id;
        $newPos = (int) $newPos;

        $curPos = $this->getDetails($id);
        $cats = $this->getSubCatsAsArray($this->getParentId($id));


        $pos = array();
        foreach($cats as $k=>$v){
            if($v['id']==$id) continue;
            $pos[(int)$v['sort']] = $v['id'];
        };

        $pos = array_regenerate_keys($pos);

        $pos = array_insert($pos, $newPos, $id);

        foreach($pos as $sort=>$id){
            $DB->update($this->tablename, array('sort'=>$sort), "id = {$id}");
        };

        return true;




    }

    /**
     *  Move a category under a new parent category
     */
    public function move($catId, $newParent, $newPos = NULL)
    {
        $DB = $this->db;
        $catId = (int) $catId;
        $newParent = (int) $newParent;
        $newPos = (int) $newPos;

        // check that the category is not being moved under a descendant


        $ancestor = $newParent;
        while($ancestor!=0){
            if($ancestor == $catId){
              // can not move a cat under one of its descendants
              return false;
        };

        // new depthnew dept    };
            $ancestor = $this->getParentId($ancestor);
        };
        $parentDepth = $this->getCategoryDepth($newParent);
        if($parentDepth >= $this->maxDepth){
            // max depth exceeded
            return false;
        };

        $oldParent = $this->getParentId($catId);

        $DB->update($this->tablename, array('parent_id'=>$newParent, 'depth'=>$parentDepth+1), 'id = '.$catId);

        $select = $DB->select()->from($this->tablename, 'COUNT(*)')->where('parent_id', $newParent);
        $numSubCats = $DB->fetchAll($select);
        $numSubCats = $numSubCats[0]['COUNT(*)'];
        $DB->update($this->tablename, array('num_sub_cats'=>$numSubCats), 'id = '.$newParent);

        $select = $DB->select()->from($this->tablename, 'COUNT(*)')->where('parent_id', $oldParent);
        $numSubCats = $DB->fetchRow($select);
        $numSubCats = $numSubCats['COUNT(*)'];
        $DB->update($this->tablename, array('num_sub_cats'=>$numSubCats), 'id = '.$oldParent);

        if($newPos != NULL) $this->repositionCat($catId, $newPos);
        return true;
    }

    /**
     *  Use this to update the number of items listed in a category
     */
    public function incrementNumItems($catId)
    {
        $DB = $this->db;
        $catId = (int) $catId;
        $select = $DB->select();
        $select->from($this->tablename, 'num_items, parent_id');
        $select->where('id', $catId);
        $res = $DB->fetchRow($select);
        $DB->update($this->tablename, array('num_items'=>$res['num_items']+1), 'id = '.$catId);
        if($res['parent_id']!=0){
            $this->incrementNumItems($res['parent_id']);
        };
    }

    /**
     *  decrease the number of items for a category
     */
    public function decrementNumItems($catId)
    {
        $DB = $this->db;
        $catId = (int) $catId;
        $select = $DB->select();
        $select->from($this->tablename, 'num_items, parent_id');
        $select->where('id', $catId);
        $res = $DB->fetchRow($select);
        $DB->update($this->tablename, array('num_items'=>$res['num_items']-1), 'id = '.$catId);
        if($res['parent_id']!=0){
            $this->decrementNumItems($res['parent_id']);
        };
    }

    /**
     *  Recursive method to get all the parentIds and return as an array in ascending order
     *  with the top-most category at the top.
     */
    public function getAllParentCatIdsAsArray($id)
    {
        if($id==0)return array($id);
        $parent = $this->getParentId($id);
        $arr = array($id);
        $parent = $id;
        while($parent!=0){
            $parent = $this->getParentId($parent);
            array_unshift($arr, $parent);
        };
        return $arr;
    }
}
